<?php

namespace core\lib;


class container {
    public $binds;

    /**
     * @param string $abstract 接口名
     * @param string $concrete 实例名
     */
    public function bind($abstract, $concrete) {
        $this->binds[$abstract] = $concrete;
    }

    /**
     * @param string $className 接口或实例名
     * @return object 根据接口或对象,返回实例化后的对象
     * @throws \Exception
     */
    public function build($className) {
        // 如果是回调函数,则直接调用
        if ($className instanceof \Closure) {
            return $className();
        }
        // 创建反射类
        $reflector = new \ReflectionClass($className);
        // 如果是接口,则查找对应的绑定实例
        if ($reflector->isInterface()) {
            if (isset($this->binds[$className])) {
                return $this->build($this->binds[$className]);
            } else {
                throw new \Exception($className . '接口没有对应实现');
            }
        }
        // 不能实例化，抛异常
        if (!$reflector->isInstantiable()) {
            throw new \Exception("Target [$className] is not instantiable.");
        }
        // 创建构造器
        $constructor = $reflector->getConstructor();
        // 没有构造函数
        if ($constructor === null) {
            return new $className;
        }
        // 获取构造函数参数
        $dependencies = $constructor->getParameters();
        // 获取依赖实例对象
        $instances = $this->getDependencies($dependencies);
        // 实例化对象
        return $reflector->newInstanceArgs($instances);
    }

    /**
     * @param array $parameters 获取依赖
     * @return array
     * @throws \Exception
     */
    public function getDependencies(array $parameters) {
        $dependencies = [];

        foreach ($parameters as $parameter) {
            $dependency = $parameter->getClass();
            if ($dependency) {
                $dependencies[] = $this->build($dependency->name);
            }
        }
        return (array)$dependencies;
    }
}